package util;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Random;

public class Util {

	/**
	 * @return PadraoTreinamento [] - array com o padrao de treinamento.
	 */
	public static ArrayList<PadraoTreinamento> getTrainingData(
			String pNomeArquivo) {

		ArrayList<PadraoTreinamento> aTreinamento = new ArrayList<PadraoTreinamento>();

		// Dados de treinamento.
		try {
			BufferedReader in = new BufferedReader(new FileReader(pNomeArquivo));
			String str;
			while ((str = in.readLine()) != null) {
				String[] splitTreinamento = str.split(",");
				ArrayList<Double> valoresPadrao = new ArrayList<Double>();

				PadraoTreinamento padraoTreinamento = new PadraoTreinamento();
				
				for (int j = 0; j < splitTreinamento.length - 1; j++) {
					valoresPadrao.add(Double.parseDouble(splitTreinamento[j]));
				}

				padraoTreinamento.setValoresPadrao(valoresPadrao);
				padraoTreinamento
						.setClasse(Integer
								.parseInt(splitTreinamento[splitTreinamento.length - 1]));
				aTreinamento.add(padraoTreinamento);
			}
			in.close();
		} catch (IOException e) {
			System.err.println("Unable to read from file!");
		}
		return aTreinamento;
	}

	public static void montarArquivoKFoldValidation(String pCaminho, String pNomeArquivo, String pSufixo, int pKValue, int indexForValidation) {

		try {
			BufferedWriter out = new BufferedWriter(new FileWriter(pCaminho + pSufixo + "_" + indexForValidation));
			System.out.println("Criando: " + pSufixo + "_"+ indexForValidation);
			
			for (int i = 0; i < pKValue; i++) {
				if (indexForValidation != i) {
					BufferedReader in = new BufferedReader(new FileReader(pCaminho + pNomeArquivo+i));
					System.out.println("Lendo: "+pNomeArquivo+i+".");
					String str;
					
					while ((str = in.readLine()) != null) {
						out.write(str+"\n");
					}
					in.close();
				}
			}
			out.close();
		} catch (Exception e) {
			// TODO: handle exception
		}
	}

	
	public static Double getDistanciaEuclidiana(PadraoTreinamento pPadraoA, PadraoTreinamento pPadraoB) {
		Iterator<Double> atributosA = pPadraoA.getValoresPadrao().iterator();
		Iterator<Double> atributosB = pPadraoB.getValoresPadrao().iterator();
		double euclideanDistance = 0.0;
		
		while (atributosA.hasNext()) {
		        Double valorAtributoA = atributosA.next();
		        Double valorAtributoB = atributosB.next();

		        euclideanDistance += Math.pow(Math.abs((valorAtributoA.doubleValue() - valorAtributoB.doubleValue())), 2);
		}
		
		euclideanDistance = Math.sqrt(euclideanDistance);
		
		return new Double(euclideanDistance);
	}
	
	public static PadraoTreinamento getPadraoMaisProximo(PadraoTreinamento pPadrao, ArrayList<PadraoTreinamento> arrayPadroes) {
		Double menorDistancia = 1000000.0d;
		PadraoTreinamento padraoMaisProximo = null;
		
		Iterator<PadraoTreinamento> itArray = arrayPadroes.iterator();
		while(itArray.hasNext()) {
			PadraoTreinamento padraoTeste = itArray.next();
			Double distancia = getDistanciaEuclidiana(pPadrao, padraoTeste);
			if (distancia < menorDistancia) {
				menorDistancia = distancia;
				padraoMaisProximo = padraoTeste;
			}
		}
		
		return padraoMaisProximo;
	}

	public static PadraoTreinamento getPadraoMaisProximo(PadraoTreinamento pPadrao, PadraoTreinamento[] arrayPadroes) {
		Double menorDistancia = 1000000.0d;
		PadraoTreinamento padraoMaisProximo = null;
		
		for (int i = 0; i < arrayPadroes.length; i++) {
			if(arrayPadroes[i] != null) {
				PadraoTreinamento padraoTeste = arrayPadroes[i];
				Double distancia = getDistanciaEuclidiana(pPadrao, padraoTeste);
				if (pPadrao.getClasse() != padraoTeste.getClasse()) {
					if (distancia < menorDistancia) {
						menorDistancia = distancia;
						padraoMaisProximo = padraoTeste;
					}	
				}
			}
		}
		
		return padraoMaisProximo;
	}
	
	public static PadraoTreinamento getPadraoMaisProximoQualquerClasse(PadraoTreinamento pPadrao, PadraoTreinamento[] arrayPadroes) {
		Double menorDistancia = 1000000.0d;
		PadraoTreinamento padraoMaisProximo = null;
		
		for (int i = 0; i < arrayPadroes.length; i++) {
			if(arrayPadroes[i] != null) {
				PadraoTreinamento padraoTeste = arrayPadroes[i];
				Double distancia = getDistanciaEuclidiana(pPadrao, padraoTeste);
				if (distancia < menorDistancia) {
					menorDistancia = distancia;
					padraoMaisProximo = padraoTeste;
				}	
			}
		}
		return padraoMaisProximo;
	}
	
	public static int getIndicePadraoMaisProximoQualquerClasse(PadraoTreinamento pPadrao, PadraoTreinamento[] arrayPadroes) {
		Double menorDistancia = 1000000.0d;
		int indicePadraoProximo = 0;		
		
		for (int i = 0; i < arrayPadroes.length; i++) {
			if(arrayPadroes[i] != null) {
				PadraoTreinamento padraoTeste = arrayPadroes[i];
				Double distancia = getDistanciaEuclidiana(pPadrao, padraoTeste);
				if (distancia < menorDistancia) {
					menorDistancia = distancia;
					indicePadraoProximo = i;
				}	
			}
		}
		return indicePadraoProximo;
	}
	
	public static PadraoTreinamento getPadraoMaisProximoMesmaClasse(PadraoTreinamento pPadrao, PadraoTreinamento[] arrayPadroes) {
		Double menorDistancia = 1000000.0d;
		PadraoTreinamento padraoMaisProximo = null;
		
		for (int i = 0; i < arrayPadroes.length; i++) {
			if(arrayPadroes[i] != null) {
				PadraoTreinamento padraoTeste = arrayPadroes[i];
				Double distancia = getDistanciaEuclidiana(pPadrao, padraoTeste);
				if (pPadrao.getClasse() == padraoTeste.getClasse()) {
					if (distancia < menorDistancia) {
						menorDistancia = distancia;
						padraoMaisProximo = padraoTeste;
					}	
				}
			}
		}
		
		return padraoMaisProximo;
	}
	
	public static PadraoTreinamento getPadraoAleatorio(ArrayList<PadraoTreinamento> arrayT, boolean isRemover) {
		int tamanhoDataset = arrayT.size();
		int numeroAleatorio = tamanhoDataset + 1;
		Random random = new Random();
		
		numeroAleatorio = random.nextInt(tamanhoDataset);
		
		if (isRemover) {
			return arrayT.remove(numeroAleatorio);			
		} else {
			return arrayT.get(numeroAleatorio);
		}
	}

	public static int getMenorValor(Double[] array) {
		Double menorDistancia = new Double(1000000.0d);
		int indice = 0;
		
		for (int i = 0; i < array.length; i++) {
			Double distancia = array[i];
			if (distancia.doubleValue() < menorDistancia.doubleValue()) {
				indice = i;
			}
		}
		
		return indice;
	}
	
	public static int getMenorValor(double[] array, PadraoTreinamento[] pArrayListPadroes) {
		double menorDistancia = 1000000.0d;
		int indice = 0;
		
		for (int i = 0; i < array.length; i++) {
			double distancia = array[i];
			if (distancia < menorDistancia) {
				if(pArrayListPadroes[i] != null) {
					menorDistancia = distancia;
					indice = i;
				} 
			}
		}
		
		return indice;
	}
}
